use std::{ops::BitAnd, any::Any};

macro_rules! bit {
    ($x:expr) => {
        1 << $x
    };
}

#[derive(Debug, PartialEq, Eq)]
pub enum EventType {
    None,
    WindowClose,
    WindowResize,
    WindowFocus,
    WindowLostFocus,
    WindowMove,
    AppTick,
    AppUpdate,
    AppRender,
    KeyPressed,
    KeyReleased,
    KeyTyped,
    MouseButtonPressed,
    MouseButtonReleased,
    MouseMoved,
    MouseScrolled,
}

pub enum EventCategory {
    None = 0,
    EventCategoryApplication = bit!(0), // 1
    EventCategoryInput = bit!(1),       // 4
    EventCategoryKeyboard = bit!(2),    // 8
    EventCategoryMouse = bit!(3),       // 16
    EventCategoryMouseButton = bit!(4), // 32
}

impl BitAnd for EventCategory {
    type Output = bool;

    fn bitand(self, rhs: Self) -> Self::Output {
        (self) & (rhs)
    }
}

pub trait StaticEvent {
    fn get_static_event_type() -> EventType;
}

pub trait Event: Any  {
    fn get_event_type(&self) -> EventType;

    fn get_name(&self) -> String;

    fn get_event_category(&self) -> EventCategory;

    fn is_category(&self, category: EventCategory) -> bool;

    fn set_handle(&mut self, handle: bool);

    fn get_handle(&self) -> bool;

    fn as_any(&self) -> &dyn Any;
    fn as_any_mut(&mut self) -> &mut dyn Any;
}

#[macro_export]
macro_rules! set_handle {
    ($field:expr, $value:expr) => {
        $field = $value;
    };
    ($($else:tt)*) => {};
}

#[macro_export]
macro_rules! event_info {
    (
        $vis1:vis struct $name:ident {
            $(
                $vis2:vis $item:ident: $t:tt$(,)*
            ),+
        },
        $type:expr,
        $category:expr
    ) => {
        $vis1 struct $name {
            $(
                $vis2 $item: $t,
            )+
        }

        impl StaticEvent for $name {
            fn get_static_event_type() -> EventType {
                $type
            }
        }

        impl Event for $name {

            fn get_event_type(&self) -> EventType {
                $type
            }

            fn get_name(&self) -> String {
                format!("{:?}",$type)
            }

            fn get_event_category(&self) -> EventCategory {
                $category
            }

            fn is_category(&self, category:EventCategory ) -> bool {
                self.get_event_category() & category
            }

            fn set_handle(&mut self , handle:bool) {
                self.handle = handle;
            }

            fn get_handle(&self) -> bool {
                self.handle
            }

            fn as_any(&self) -> &dyn std::any::Any {
                self as &dyn std::any::Any
            }
        
            fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
                self as  &mut dyn std::any::Any
            }

            

        }
    };
}

pub struct EventDispatch<'a> {
    event: &'a mut Box<dyn Event>,
}

impl<'a> EventDispatch<'a> {
    pub fn new(event: &'a mut Box<dyn Event>) -> Self {
        Self { event }
    }

    pub fn dispatch<T,F>(&mut self, mut cb: F) -> bool
    where 
        T: StaticEvent + Event,
        F: FnMut(&mut T) -> bool
    {
   
        if T::get_static_event_type() == self.event.get_event_type() {
            if let Some(event) = self.event.as_any_mut().downcast_mut::<T>() {
                let handle = cb(event);

                if handle {
                    self.event.set_handle(handle);
                }
            }
           
            return true;
        }
        return false;
    }
}

